# -*- coding:utf-8 -*-
"""
Created on May 1, 2011

@author: Lithuanian
"""
from multi_robot_problem import MultiRobotState
from time import time
from manhattanHeuristic import manhattenSumHuristics
from astar import AStar
from pile2robotHeuristic import pile2robot
from manAndRatioHeuristic import mixed, mixedWithManSum
from manAndRatioHeuristic import avoid
from Tester import Tester
from anytimeAlgorithms import AnytimeBestFirstGraphSearch
from anytimeAlgorithms import AnytimeAstarGraphSearch
from anytimeAlgorithms import AnytimeRandRestBF
from manAndNumOfPiles import ManAndPiles

class ExperimentRunner:    
    #ALGO
    BF          = "Best First"
    BF_ANYTIME  = "Anytime Best First"
    A           = "Astar"
    A_ANYTIME   = "Anytime Astar"
    
    #Heuristic
    MAN_SUM             = "Manhattan Sum"
    MIXED               = "Mixed"
    MIXED_AND_MAN_SUM   = "Mixed with Manhattan Sum"
    MAN_AND_PILES       = "Manhattan Sum with Piles"
    AVOID               = "Avoid and manhattan"
    
    #consts
    AlgoType        = 'AlgoType'
    Algo            = 'algo'
    Heuristic       = 'heuristic'
    HeuristicName   = 'heuristicName'
    
    PASSED_TIME     = 'time'
    SOLUTION        = 'solution'
    IS_ANYTIME      = 'isAnytime'
    
    def outerDelaConfig(self):
        configs = []
        config = dict()
        for i in xrange(3, 10, 2):
            #---Best first: Mixed + man solution---
            config[self.AlgoType]       = self.BF_ANYTIME
            config[self.Algo]           = AnytimeBestFirstGraphSearch()
            config[self.Heuristic]      = mixedWithManSum(float(i)/10)
            config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM + '(d =' + str(float(i)/10) + ')'
            configs.append(config.copy())
        return configs
    
    def outerDeltaExpiriment(self):
        expName = "OuterDelta"
        self.tester = Tester(8, 12, 8, 12, 3, 18, 6, Tester.OBSTACLES_ALL, 4,2,4,2,1,1)
        configs = self.outerDelaConfig()
        self.prepareExpertimentAux(expName, 50, 40, 40, 0.7, configs)
        
    def standartConfigs(self):
        configs = []
        config = dict()
        #---Best first: manhattan solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeBestFirstGraphSearch()
        config[self.Heuristic]      = ManAndPiles()
        config[self.HeuristicName]  = self.MAN_AND_PILES
        configs.append(config.copy())
        #---Best first: Mixed solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeBestFirstGraphSearch()
        config[self.Heuristic]      = mixed()
        config[self.HeuristicName]  = self.MIXED
        configs.append(config.copy())
        #---Best first: Mixed + man solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeBestFirstGraphSearch()
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        #---A*: man solution---
        config[self.AlgoType]       = self.A
        config[self.Algo]           = AStar()
        config[self.Heuristic]      = manhattenSumHuristics()
        config[self.HeuristicName]  = self.MAN_SUM
        configs.append(config.copy())
        #---A*: mixed + man solution---
        config[self.AlgoType]       = self.A
        config[self.Algo]           = AStar()
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        return configs
    
    def mediumConfigs(self):
        configs = self.standartConfigs()
        config = dict()
        #---Best first: avoid + man solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeBestFirstGraphSearch()
        config[self.Heuristic]      = avoid()
        config[self.HeuristicName]  = self.AVOID
        configs.append(config.copy())
        #---A*: avoid + man solution---
        config[self.AlgoType]       = self.A
        config[self.Algo]           = AStar()
        config[self.Heuristic]      = avoid()
        config[self.HeuristicName]  = self.AVOID
        configs.append(config.copy())
        return configs
    
    def anytimeConfigs(self):
        config = dict()
        configs = self.bfAnytimeConfigs()
        #---A* anytime: mixed + man solution---
        for i in xrange(0.1,0.91,0.2):
            config[self.AlgoType]       = self.A_ANYTIME + " with " + str(i) + " phase time"
            config[self.Algo]           = AnytimeAstarGraphSearch()
            config[self.Heuristic]      = mixedWithManSum()
            config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
            configs.append(config.copy())
        return configs
    
    def bfAnytimeConfigs(self):
        configs = []
        config = dict()
        #---Best first anyTime: mixed + man solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeRandRestBF()
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        return configs
    
    def anytimeAstarWithPhaseConfigs(self):
        configs = []
        config = dict()
        #---A* anytime: mixed + man solution---
        for i in xrange(3,10,2):
            config[self.AlgoType]       = self.A_ANYTIME
            config[self.Algo]           = AnytimeAstarGraphSearch(float(i)/10)
            config[self.Heuristic]      = mixedWithManSum()
            config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM + "_with_" + str(i) + "_phase"
            configs.append(config.copy())
        return configs
    
    def hardConfigs(self):
        configs = []
        config = dict()
        #---Best first: Mixed + man solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeBestFirstGraphSearch()
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        #---Best first: Mixed solution---
        config[self.AlgoType]       = self.BF
        config[self.Algo]           = AnytimeBestFirstGraphSearch()
        config[self.Heuristic]      = mixed()
        config[self.HeuristicName]  = self.MIXED
        configs.append(config.copy())
        #---A*: mixed + man solution---
        config[self.AlgoType]       = self.A
        config[self.Algo]           = AStar()
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        return configs
    
    def bfVSastarConfigs(self):
        configs = []
        config = dict()
        #---Best first: Mixed + man solution---
        config[self.AlgoType]       = self.BF_ANYTIME
        config[self.Algo]           = AnytimeRandRestBF()
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        config[self.AlgoType]       = self.A_ANYTIME
        config[self.Algo]           = AnytimeAstarGraphSearch(0.7)
        config[self.Heuristic]      = mixedWithManSum()
        config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM
        configs.append(config.copy())
        return configs
    
    def pilesPenaltyConfigs(self):
        configs = []
        config = dict()
        #---Best first: Mixed + man solution---
        config[self.AlgoType]       = self.BF_ANYTIME
        config[self.Algo]           = AnytimeRandRestBF()
        for i in xrange(3,10,3):
            config[self.Heuristic]      = mixedWithManSum(None, i, True)
            config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM + "_with_" + str(i) + "_Mul_penalty"
            configs.append(config.copy())
        for i in xrange(1,4,1):
            config[self.Heuristic]      = mixedWithManSum(None, i, False)
            config[self.HeuristicName]  = self.MIXED_AND_MAN_SUM + "_with_" + str(i) + "_Pow_penalty"
            configs.append(config.copy())
        return configs
        
        configs.append(config.copy())
        return configs 
    
    def easyProblems(self):
        '''
        Easy experiments - small board, three robots, and random obstacles
        '''
        expName = "easy"
        self.tester = Tester(5, 10, 5, 10, 3, 15, 4, Tester.OBSTACLES_RAND, 4,2,4,2,0,0)
        configs = self.standartConfigs()
        self.prepareExpertimentAux(expName, 186, 20, 40, 0.7, configs)
    
    def mediumProblems(self):
        '''
        Medium experiments - medium boards, three robots and rand obstacles
        '''
        expName = "medium"
        self.tester = Tester(10, 13, 10, 13, 3, 18, 5, Tester.OBSTACLES_ALL, 4,2,4,2,0,0)
        configs = self.mediumConfigs()
        self.prepareExpertimentAux(expName, 200, 20, 40, 0.7, configs)
        
    def hardProblems(self):
        '''
        Hard experiments - mesium boards, four robots and rand obstacles 1 pocket and 1 wall at least
        '''
        expName = "hard"
        self.tester = Tester(12, 15, 12, 15, 4, 22, 7, Tester.OBSTACLES_ALL, 5,2,4,2,1,1)
        configs = self.hardConfigs()
        self.prepareExpertimentAux(expName, 50, 20, 40, 0.7, configs)
    
    
    def bfanytimeExperiment(self):
        expName = "bf_anytime"
        self.tester = Tester(12, 15, 12, 15, 4, 25, 7, Tester.OBSTACLES_ALL, 5,2,4,2,1,1)
        configs = self.bfAnytimeConfigs()
        self.prepareExpertimentAux(expName, 50, 20, 40, 0.7, configs, True, [10,20,30,40,50])
        
    def astarPhaseExperiment(self):
        expName = "astar_phase"
        self.tester = Tester(12, 15, 12, 15, 4, 25, 7, Tester.OBSTACLES_ALL, 5,2,4,2,1,1)
        configs = self.anytimeAstarWithPhaseConfigs()
        self.prepareExpertimentAux(expName, 100, 20, 40, 0.7, configs, True, [10,20,30,40,50])
        
    def bfVSastar40secsExperiment(self):
        expName = "astarVSbf_40sec"
        self.tester = Tester(13, 16, 13, 16, 4, 27, 8, Tester.OBSTACLES_ALL, 5,2,4,2,1,1)
        configs = self.bfVSastarConfigs()
        self.prepareExpertimentAux(expName, 100, 20, 40, 0.7, configs, True, [40])
    
    def pilesPenaltyExperiment(self):
        expName = "pilesPenalty"
        self.tester = Tester(13, 16, 13, 16, 4, 27, 8, Tester.OBSTACLES_ALL, 5,2,4,2,1,1)
        configs = self.pilesPenaltyConfigs()
        self.prepareExpertimentAux(expName, 100, 20, 40, 0.7, configs, True, [40])
        
    def anytimebfTimes(self):
        expName = "bf_2nd_sol"
        self.tester = Tester(12, 15, 12, 15, 4, 25, 7, Tester.OBSTACLES_ALL, 5,2,4,2,1,1)
        configs = self.bfAnytimeConfigs()
        self.prepareExpertimentAux(expName, 100, 20, 40, 0.7, configs, False)
        
    def prepareExpertimentAux(self, expName, numOfProblems, timeBF, timeAstar, \
                              phaseAstar, configs, isAnytime = False, expTimes = None):
        '''
        Prapare experiment - print messages, open result and problem files (and close them at the end)
        '''
        resultFile  = open("C:\Users\Lithuanian\Desktop\AIresults\\" + expName + "_results.txt","w")
        problemFile = open("C:\Users\Lithuanian\Desktop\AIresults\\" + expName + "_problems.txt","w")
        print "Starting ", expName ," Experiment, wish me luck!"
        self.experiment(numOfProblems, configs, timeBF, timeAstar, phaseAstar, resultFile,\
                         problemFile, expName, isAnytime, expTimes)
        print "DONE!"
        resultFile.close()
        problemFile.close()
    
    def run(self, problem, config, timeAstar, phaseAstar, timeBF, i):
        '''
        Run problem configuration and retrive result
        @return: Result object
        '''
        print "--" + config[self.AlgoType] + " with " + config[self.HeuristicName]
        problemResObj = self.solveProblemInConfiguration(config, problem, timeAstar, phaseAstar, timeBF) 
            
        #create results
        if problemResObj[self.SOLUTION] is None:
            solutionLen = -1
        else:
            solutionLen = len(problemResObj[self.SOLUTION])
        
        print "Solution Length: " + str(solutionLen) + ", Time: " + str(problemResObj[self.PASSED_TIME]) 
        result = Result(config[self.AlgoType],config[self.HeuristicName], \
                        problemResObj[self.PASSED_TIME], solutionLen, problemResObj[self.IS_ANYTIME], i)
        return result
    
    def experiment(self, numberOfProblems, configs, timeBF, timeAstar, phaseAstar, \
                   resultFile, problemFile, expName, isAnyTimeExperiment = False, problemTimes = None):
        self.printResultHeadline(resultFile)
        for i in xrange(0, numberOfProblems):
            problemResults = []
            #generate problem
            print "\nProblem #" + str(i) + " from total " + str(numberOfProblems) + " problems"
            problem = None
            while problem is None:
                problem = self.tester.generateProblemSpace()
            print problem
            self.tester.printProblemSpaceParams(problem)
            #log problem
            self.logProblem(problem, i, problemFile)
            
            #solve problem with each algo + heuristic configuraion
            for config in configs:
                if isAnyTimeExperiment:
                    name = config[self.HeuristicName]
                    for t in problemTimes:
                        config[self.HeuristicName] = config[self.HeuristicName]+ "_time_"+ str(t)
                        result = self.run(problem, config, t, phaseAstar, t, i)
                        problemResults.append(result)
                        config[self.HeuristicName] = name
                else:
                    result = self.run(problem, config, timeAstar, phaseAstar, timeBF, i)
                    problemResults.append(result)
                    
            #log results.
            #if False returned - *none* of the alogorithms found a solution - assume the problem is not solvable
            if self.analyzeResults(problemResults):
                #problem is solvable - log it
                self.logResults(problemResults, expName)                
            
    
    def logResults(self, results,expName):
        '''
        Write results to file
        '''
        for r in results:
            file = open("C:\Users\Lithuanian\Desktop\AIresults\\" + \
                        expName + "_" + r.algo + "_" + r.heuri + ".txt", 'a')
            file.writelines(str(r)+"\n")
            file.close()
    
    def logProblem(self, problem, problemNum, file):
        '''
        print :'# {problemNum}
        prolem board
        problem params
        run command
        '''
        problemString = "# " + str(problemNum) + "\n" + str(problem) + "\n" + self.tester.problemSpace2String(problem)  + "\n"
        file.writelines(problemString)
            
    
    def analyzeResults(self, results):
        '''
        @param results: list of result objects
        finds problem winner and solution quality 
        '''
        bestSolution = None
        #find winner and best solution
        for r in results:
            if r.sol != -1 and (bestSolution is None or r.sol < bestSolution):
                bestSolution = r.sol
        
        #No solution found
#        if bestSolution == None:
#            return False
        
        #set winner(s) and solution quality for eash result
        for r in results:
            if r.sol == bestSolution:
                r.isWinner = True
            if r.sol != -1:
                r.quality = float(bestSolution)/r.sol
            else:
                r.quality = 0
        return True
    
    def solveProblemInConfiguration(self, config, problem, timeAstar, phaseAstar, timeBF):
        '''
        Solve single problem with the given configuration
        @param config: configuration
        @param problem: 
        '''
        solution = None 
        isAnytime = False
        if config[self.AlgoType] == self.BF_ANYTIME or config[self.AlgoType] == self.A_ANYTIME:
            isAnytime = True
            #anytime algos - set solver time
            if config[self.AlgoType] == self.BF_ANYTIME:
                start = time()
                solution = config[self.Algo].find(problem, config[self.Heuristic], timeBF)
                end = time()
            else:
                start = time()
                solution = config[self.Algo].find(problem, config[self.Heuristic], timeAstar)
                end = time()
        else:
            if config[self.AlgoType] == self.BF:
                #BF
                start = time()
                solution = config[self.Algo].find(problem, config[self.Heuristic], None)
                end = time()
            else:
                #a*
                start = time()
                solution = config[self.Algo].find(problem, config[self.Heuristic])
                end = time()
        
        res = dict()
        res[self.PASSED_TIME]   = end - start
        res[self.SOLUTION]      = solution
        res[self.IS_ANYTIME]    = isAnytime
        return res
    
    def printResultHeadline(self, file):
        file.writelines("Algo" + "\t" + "Heuristics" + "\t" + "Time" + "\t" +\
                         "Solution Length" + "\t" + "isAnytime" + "\t" + "Problem Number" + "\t" + "is Winner" + "\t" + "Quality" + "\n")
        
    def parseResultFile(self, filePath, isCahngeFailed = False):
        '''
        Return a list of values from the file - each entry is a line in the file.
        '''
        list = []
        file = open(filePath, 'r')
        max = None
        for line in file:
            i = 0
            for c in line:
                if c == '\n':
                    break
                i += 1
            value = int(line[0:i:1])
            if max is None or max < value:
                max = value
            list.append(value)
        
        if isCahngeFailed:
            #changed failed (-1) to max value in the experiment
            for l in line:
                if l == -1:
                    l = max
        file.close()
        return list
    
    RES_NAME = "name"
    RES_LIST = "list"
    
    def easyResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\easy\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "easy_Astar_Manhattan Sum.txt")
        res[self.RES_NAME] = 'Astar_Manhattan_Sum'
        results.append(res.copy())
        #----------
        res[self.RES_LIST] = self.parseResultFile(base + "easy_Astar_Mixed with Manhattan Sum.txt")
        res[self.RES_NAME] = 'Astar_Mixed with Manhattan Sum'
        results.append(res.copy())
        #----------
        res[self.RES_LIST] = self.parseResultFile(base + "easy_Best First_Manhattan Sum with Piles.txt")
        res[self.RES_NAME] = 'Best First_Manhattan Sum with Piles'
        results.append(res.copy())
        #----------
        res[self.RES_LIST] = self.parseResultFile(base + "easy_Best First_Mixed with Manhattan Sum.txt")
        res[self.RES_NAME] = 'Best First_Mixed with Manhattan Sum'
        results.append(res.copy())
        #----------
        res[self.RES_LIST] = self.parseResultFile(base + "easy_Best First_Mixed.txt")
        res[self.RES_NAME] = 'Best First_Mixed'
        results.append(res.copy())
        return results
    
    def mediumResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\medium\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Astar_Avoid and manhattan.txt")
        res[self.RES_NAME] = 'Astar_Avoid and manhattan'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Astar_Manhattan Sum.txt")
        res[self.RES_NAME] = 'Astar_Manhattan Sum'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Astar_Mixed with Manhattan Sum.txt")
        res[self.RES_NAME] = 'Astar_Mixed with Manhattan Sum'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Best First_Avoid and manhattan.txt")
        res[self.RES_NAME] = 'Best First_Avoid and manhattan'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Best First_Manhattan Sum with Piles.txt")
        res[self.RES_NAME] = 'Best First_Manhattan Sum with Piles'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Best First_Mixed with Manhattan Sum.txt")
        res[self.RES_NAME] = 'Best First_Mixed with Manhattan Sum'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "medium_Best First_Mixed.txt")
        res[self.RES_NAME] = 'Best First_Mixed'
        results.append(res.copy())
        return results
    
    def hardResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\hard\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "hard_Astar_Mixed with Manhattan Sum.txt")
        res[self.RES_NAME] = 'Astar_Mixed with Manhattan Sum'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "hard_Best First_Mixed with Manhattan Sum.txt")
        res[self.RES_NAME] = 'Best First_Mixed with Manhattan Sum'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "hard_Best First_Mixed.txt")
        res[self.RES_NAME] = 'Best First_Mixed'
        results.append(res.copy())
        return results
    
    def outerDResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\outerDelta\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "d=0.3.txt")
        res[self.RES_NAME] = 'd=0.3'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "d=0.5.txt")
        res[self.RES_NAME] = 'd=0.5'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "d=0.7.txt")
        res[self.RES_NAME] = 'd=0.7'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "d=0.9.txt")
        res[self.RES_NAME] = 'd=0.9'
        results.append(res.copy())
        return results
    
    def astarVSbfResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\astarVSbf\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "astar.txt")
        res[self.RES_NAME] = 'astar'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "bf.txt")
        res[self.RES_NAME] = 'bf'
        results.append(res.copy())
        return results
    
    def phaseResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\astarPhase\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "0.3.txt")
        res[self.RES_NAME] = '0.3'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "0.5.txt")
        res[self.RES_NAME] = '0.5'
        results.append(res.copy())
        return results
        
    def mulPowResults(self):
        results = []
        res = dict()
        base = "C:\\Users\\Lithuanian\\Desktop\\AIresults\\pilesPenalty\\res\\"
        res[self.RES_LIST] = self.parseResultFile(base + "Mul3.txt")
        res[self.RES_NAME] = 'mul3'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "Mul9.txt")
        res[self.RES_NAME] = 'mul9'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "Mul6.txt")
        res[self.RES_NAME] = 'mul6'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "Pow1.txt")
        res[self.RES_NAME] = 'Pow1'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "Pow2.txt")
        res[self.RES_NAME] = 'Pow2'
        results.append(res.copy())
        #------
        res[self.RES_LIST] = self.parseResultFile(base + "Pow3.txt")
        res[self.RES_NAME] = 'Pow3'
        results.append(res.copy())
        return results
            
class Result:
    def __init__(self, algo, heuristics, time, solutionLen, isAnyTime, experimentNum, isWinner = False, quality = None):
        self.algo           = algo
        self.heuri          = heuristics 
        self.time           = time
        self.sol            = solutionLen
        self.anyTime        = isAnyTime
        self.experimentNum  = experimentNum
        self.isWinner       = isWinner
        #quality is defined as: optimalSolution/currentSolution (when optimal = best found solution
        self.quality        = quality
    
    def __str__(self):
        isWinner = 0
        if self.isWinner:
            isWinner = 1
        if self.sol != -1:
            return self.algo + "\t" + self.heuri + "\t" + str(self.time) + "\t" + str(self.sol) + "\t" + str(self.anyTime) \
                     + "\t" + str(self.experimentNum) + "\t" + str(isWinner) + "\t" + str(self.quality)
        else:  
            return self.algo + "\t" + self.heuri + "\t" + str(self.time) + "\t" + "\t" + str(self.anyTime) \
                     + "\t" + str(self.experimentNum) + "\t" + str(isWinner) + "\t" + str(self.quality) + "\t1"
    
    def copy(self):
        return Result(self.algo, self.heuri, self.time, self.sol, self.anyTime, self.experimentNum, self.isWinner, self.quality)